package blockchain

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"

	myconfig "backend/internal/config"
	"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
	"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
)

// Client 结构体封装了与区块链网络交互的功能
type Client struct {
	gateways     map[string]*gateway.Gateway     // 各个链的网关连接
	networks     map[string]*gateway.Network     // 各个链的网络连接
	chainConfigs map[string]myconfig.ChainConfig // 链的配置信息
	mutex        sync.RWMutex                    // 读写锁保证并发安全
}

// NewClient 创建并初始化区块链客户端
func NewClient(chainsConfig map[string]myconfig.ChainConfig) (*Client, error) {
	log.Printf("正在初始化链客户端，共 %d 条链", len(chainsConfig))

	client := &Client{
		gateways:     make(map[string]*gateway.Gateway),
		networks:     make(map[string]*gateway.Network),
		chainConfigs: make(map[string]myconfig.ChainConfig),
	}

	for chainID, cfg := range chainsConfig {
		log.Printf("开始初始化链: %s", chainID)

		// 确保配置文件路径是绝对路径
		absProfilePath, err := filepath.Abs(cfg.Connection.Profile)
		if err != nil {
			log.Printf("链 %s 获取配置文件绝对路径失败: %v", chainID, err)
			return nil, fmt.Errorf("链 %s 获取配置文件绝对路径失败: %v", chainID, err)
		}

		// 初始化钱包目录
		if err := initWallet(chainID, cfg, absProfilePath); err != nil {
			log.Printf("链 %s 钱包初始化失败: %v", chainID, err)
			return nil, fmt.Errorf("链 %s 钱包初始化失败: %v", chainID, err)
		}

		// 钱包路径
		walletPath := filepath.Join(os.TempDir(), "fabric-wallets", chainID)

		// 连接到网关
		gw, err := connectToGateway(walletPath, absProfilePath, cfg.Connection.User)
		if err != nil {
			log.Printf("链 %s 连接到网关失败: %v", chainID, err)
			return nil, fmt.Errorf("链 %s 连接到网关失败: %v", chainID, err)
		}

		log.Printf("链 %s 网关连接成功", chainID)

		// 获取网络
		network, err := gw.GetNetwork(cfg.Connection.Channel)
		if err != nil {
			gw.Close()
			log.Printf("链 %s 获取网络失败: %v", chainID, err)
			return nil, fmt.Errorf("链 %s 获取网络失败: %v", chainID, err)
		}

		log.Printf("链 %s 网络获取成功 | 通道: %s", chainID, cfg.Connection.Channel)

		// 保存连接
		client.gateways[chainID] = gw
		client.networks[chainID] = network
		client.chainConfigs[chainID] = cfg

		log.Printf("链 %s 客户端初始化完成", chainID)
	}

	log.Printf("所有链客户端初始化完成，共成功初始化 %d 条链", len(chainsConfig))
	return client, nil
}

// initWallet 初始化钱包并填充身份信息

// connectToGateway 连接到Hyperledger Fabric网关

// Evaluate 执行查询类型的链码调用
func (c *Client) Evaluate(chainID, chaincodeName string, args ...[]byte) ([]byte, error) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	// 获取网络连接
	network, exists := c.networks[chainID]
	if !exists {
		return nil, fmt.Errorf("链 %s 客户端不存在", chainID)
	}

	// 获取链码合约
	contract := network.GetContract(chaincodeName)

	// 记录详细的调用信息
	c.logInvocation("评估", chainID, chaincodeName, args)

	// 将字节参数转换为字符串参数
	strArgs := make([]string, len(args))
	for i, arg := range args {
		strArgs[i] = string(arg)
	}

	// 评估交易
	result, err := contract.EvaluateTransaction(strArgs[0], strArgs[1:]...)
	if err != nil {
		log.Printf("链 %s | 链码 %s | 评估事务失败: %v", chainID, chaincodeName, err)
		return nil, fmt.Errorf("评估事务失败: %v", err)
	}

	log.Printf("链 %s | 链码 %s | 评估事务成功", chainID, chaincodeName)
	return result, nil
}

// SubmitTransaction 执行写入类型的链码调用
func (c *Client) SubmitTransaction(chainID, chaincodeName string, args ...[]byte) ([]byte, error) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	// 获取网络连接
	network, exists := c.networks[chainID]
	if !exists {
		return nil, fmt.Errorf("链 %s 客户端不存在", chainID)
	}

	// 获取链码合约
	contract := network.GetContract(chaincodeName)

	// 记录详细的调用信息
	c.logInvocation("提交", chainID, chaincodeName, args)

	// 将字节参数转换为字符串参数
	strArgs := make([]string, len(args))
	for i, arg := range args {
		strArgs[i] = string(arg)
	}

	// 提交交易
	result, err := contract.SubmitTransaction(strArgs[0], strArgs[1:]...)
	if err != nil {
		log.Printf("链 %s | 链码 %s | 提交事务失败: %v", chainID, chaincodeName, err)
		return nil, fmt.Errorf("提交事务失败: %v", err)
	}

	log.Printf("链 %s | 链码 %s | 提交事务成功", chainID, chaincodeName)
	return result, nil
}

// logInvocation 记录详细的链码调用信息
func (c *Client) logInvocation(action, chainID, chaincodeName string, args [][]byte) {
	log.Printf("链 %s | 链码 %s | %s事务: 函数名=%s (参数数=%d)",
		chainID, chaincodeName, action, string(args[0]), len(args)-1)

	// 尝试解析第一个参数是否为JSON（可能是结构化数据）
	if isJSON(string(args[0])) {
		log.Printf("  函数名: (JSON数据)")
	} else {
		log.Printf("  函数名: %s", string(args[0]))
	}

	// 记录其他参数
	for i := 1; i < len(args); i++ {
		arg := string(args[i])
		if isJSON(arg) {
			log.Printf("  参数 #%d (%d 字节): (JSON数据)", i, len(args[i]))
		} else {
			log.Printf("  参数 #%d (%d 字节): %s", i, len(args[i]), truncateString(arg, 100))
		}
	}
}

// isJSON 检查字符串是否为JSON格式
func isJSON(str string) bool {
	var js json.RawMessage
	return json.Unmarshal([]byte(str), &js) == nil
}

// truncateString 截断长字符串
func truncateString(str string, maxLen int) string {
	if len(str) <= maxLen {
		return str
	}
	return str[:maxLen] + "..."
}

// Close 关闭所有网关连接
func (c *Client) Close() {
	for chainID, gw := range c.gateways {
		gw.Close()
		log.Printf("链 %s 网关连接已关闭", chainID)
	}
}
func initWallet(chainID string, cfg myconfig.ChainConfig, absProfilePath string) error {
	// 钱包路径
	walletPath := filepath.Join(os.TempDir(), "fabric-wallets", chainID)

	// 创建钱包目录
	if err := os.MkdirAll(walletPath, 0700); err != nil {
		return fmt.Errorf("创建钱包目录失败: %v", err)
	}

	wallet, _ := gateway.NewFileSystemWallet(walletPath)
	userName := cfg.Connection.User

	// 如果用户身份不存在，则创建并添加到钱包
	if !wallet.Exists(userName) {
		log.Printf("链 %s 钱包中不存在用户 %s，正在填充用户凭证", chainID, userName)

		// 修改点1: 使用配置文件所在目录作为基础
		configDir := filepath.Dir(absProfilePath)

		orgBasePath := filepath.Dir(filepath.Dir(configDir)) // 返回 organizations 目录

		// 2. 构建用户目录路径（正确的结构）
		// 标准路径：.../organizations/peerOrganizations/org1.chain2.example.com/users/User1@org1.chain2.example.com
		userBasePath := filepath.Join(
			orgBasePath,
			"peerOrganizations",
			"org1.example.com", // 使用组织域名
			"users",
			cfg.Connection.User, // 例如 "User1@org1.chain2.example.com"
		)

		certPath := filepath.Join(userBasePath, "msp", "signcerts", "cert.pem")
		keyDir := filepath.Join(userBasePath, "msp", "keystore")

		log.Printf("链 %s | 证书路径: %s", chainID, certPath)
		log.Printf("链 %s | 私钥目录: %s", chainID, keyDir)

		// 检查证书路径是否存在
		if _, err := os.Stat(certPath); os.IsNotExist(err) {
			return fmt.Errorf("证书路径不存在: %s", certPath)
		}

		// 读取证书
		certData, err := ioutil.ReadFile(certPath)
		if err != nil {
			return fmt.Errorf("读取证书失败: %v", err)
		}

		// 读取私钥文件
		keyFiles, err := ioutil.ReadDir(keyDir)
		if err != nil {
			return fmt.Errorf("读取私钥目录失败: %v", err)
		}

		if len(keyFiles) == 0 {
			return fmt.Errorf("私钥目录为空: %s", keyDir)
		}

		// 获取第一个私钥文件（通常只有一个）
		keyPath := filepath.Join(keyDir, keyFiles[0].Name())
		keyData, err := ioutil.ReadFile(keyPath)
		if err != nil {
			return fmt.Errorf("读取私钥失败: %v", err)
		}

		// 创建X.509身份
		identity := gateway.NewX509Identity("Org1MSP", string(certData), string(keyData))

		// 将身份保存到钱包
		if err := wallet.Put(userName, identity); err != nil {
			return fmt.Errorf("将用户保存到钱包失败: %v", err)
		}

		log.Printf("链 %s 用户 %s 已添加到钱包", chainID, userName)
	}

	return nil
}

// connectToGateway 连接到Hyperledger Fabric网关
func connectToGateway(walletPath, absProfilePath, userName string) (*gateway.Gateway, error) {
	// 创建文件系统钱包
	wallet, err := gateway.NewFileSystemWallet(walletPath)
	if err != nil {
		return nil, fmt.Errorf("创建钱包失败: %v", err)
	}

	// 添加详细日志
	log.Printf("正在使用配置文件连接网关: %s", absProfilePath)
	log.Printf("钱包路径: %s", walletPath)
	log.Printf("用户名: %s", userName)

	// 读取并打印连接文件内容
	if configData, err := ioutil.ReadFile(absProfilePath); err == nil {
		log.Printf("连接文件内容:\n%s", string(configData))
	} else {
		log.Printf("无法读取连接文件: %v", err)
	}

	// 连接到网关
	gw, err := gateway.Connect(
		gateway.WithConfig(config.FromFile(absProfilePath)),
		gateway.WithIdentity(wallet, userName),
	)

	if err != nil {
		return nil, fmt.Errorf("网关连接失败: %v", err)
	}

	log.Printf("网关连接成功")
	return gw, nil
}
